import {
  hexStr2Bytes,
  bytes2HexStr,
  DevicePacketCoderUtil,
  DeviceMessageSend,
  DeviceMessageReceive,
  MsgType,
  simpleUUID,
  CommonConfirmReplyMsgBody,
  DevicePacket,
  HearbeatResponseMsgBody
} from '@/utils/deviceProtocolUtil.js'
import { DeviceStatus } from '@/interface/device.js'

// 设备协议服务
export const useDeviceProtocolStore = defineStore('deviceProtocol', () => {
  // 声明插件
  const tcpServer = uni.requireNativePlugin?.('tcp-server')
  tcpServer?.registerLogPrintCallback(logText => {
    g_utils.writeLog(logText)
  })

  // 服务运行状态
  let tcpServerRunning = ref(false)

  // tcp服务监听端口
  let tcpServerPort = 31313

  // 设备会话列表
  let deviceSessionList = reactive([])

  // 设备消息回调函数，key[deviceSn-msgId] - value[callbackFunction]的映射
  let deviceMessageCallbackMap = new Map()

  // 设备链接状态变更处理回调
  let deviceConnectStatusChangedCallbackObj = null
  function registerDeviceConnectStatusChangedCallback(
    callback,
    context = null
  ) {
    if (typeof callback !== 'function') {
      throw new Error('callback must be a function')
    }
    g_utils.writeLog('注册TCP设备链接状态变更事件回调')
    let callbackFunc = callback
    deviceConnectStatusChangedCallbackObj = {
      callbackFunc,
      context
    }
  }

  // 设备链接请求处理回调
  let deviceConnectRequestCallbackObj = null
  function registerDeviceConnectRequestCallback(callback, context = null) {
    if (typeof callback !== 'function') {
      throw new Error('callback must be a function')
    }
    // g_utils.writeLog('注册设备链接请求事件回调')
    let callbackFunc = callback
    deviceConnectRequestCallbackObj = {
      callbackFunc,
      context
    }
  }
  function unregisterDeviceConnectRequestCallback() {
    deviceConnectRequestCallbackObj = null
  }

  // 启动TCP服务
  function startTcpServer() {
    g_utils.writeLog('start TCP Server')
    if (tcpServerRunning.value) {
      return
    }
    tcpServerRunning.value = true
    Object.assign(deviceSessionList, []) // 清空数组

    tcpServer.createTcpServer(
      tcpServerPort,
      {
        dataType: 'hex'
      },
      msgData => {
        g_utils.writeLog('接收的设备消息: ', msgData)
        if (msgData == null || msgData.data == null) {
            return
        }
        // HEX字符串使用空格分隔
        const result = [];
        for (let i = 0; i <  msgData.data.length; i += 2) {
            result.push( msgData.data.substring(i, i + 2));
        }
        let fullHexFrame = result.join(' ')
        // 使用正则表达式匹配所有以 7e 开头和结尾的帧
        const frames = fullHexFrame.match(/7E\s.*?\s7E/gi)
        for (let frame of frames) {
            // 一帧一帧处理
            processOnePacketFrame(msgData, frame.replace(/\s/g, ''))
        }
      },
      connectedData => {
        g_utils.writeLog('TCP客户端建立链接消息: ', connectedData)
      },
      disconnectedData => {
        g_utils.writeLog('TCP客户端断开链接消息: ', disconnectedData)
        let sessionId = disconnectedData.sessionId
        let deviceSn = ""
        let deviceAddress = ""
        const index = deviceSessionList.findIndex(
          item => item.sessionId === sessionId
        )
        if (index !== -1) {
            let session = deviceSessionList[index]
            deviceSn = session.sn
            deviceAddress = `${session.ip}:${session.port}`
          // 移除断开链接的设备会话
          deviceSessionList.splice(index, 1)
        }
        
        if (deviceConnectStatusChangedCallbackObj != null) {
          let { callbackFunc, context } = deviceConnectStatusChangedCallbackObj
          callbackFunc.call(context, {
                "status": "disconnected",
                "deviceSn": deviceSn,
                "deviceAddress": deviceAddress
            })
        }
      },
      errMsg => {
        console.error('TCP客户端异常消息：' + errMsg)
      }
    )
  }
  
  /**
   * 处理数据一个数据帧
   * @param {string} oneFrameHex 数据帧，16进制字符串
   */
  function processOnePacketFrame(msgData, oneFrameHex) {
    g_utils.writeLog('处理接收到的消息帧: ', oneFrameHex)
    // 解析设备上报数据，存储或者更新会话信息
    let packet = DevicePacketCoderUtil.decode(hexStr2Bytes(oneFrameHex))
    if (packet == null) {
      g_utils.writeLog('报文解析错误')
      return
    }
    g_utils.writeLog('接收的设备消息对象: ', packet)
    
    // 设备会话存储和刷新
    let deviceSn = packet.sn
    let deviceAddress = `${msgData.host}:${msgData.port}`
    let deviceSession = {
      sn: deviceSn,
      sessionId: msgData.sessionId,
      ip: msgData.host,
      port: parseInt(msgData.port),
      lastActiveTime: new Date(),
      seq: 0,
      nextSeq: function () {
        if (this.seq >= 0xff) {
          this.seq = 0
        }
        this.seq += 1
        return this.seq
      }
    }
    const index = deviceSessionList.findIndex(item => item.sn === deviceSn)
    let deviceInfoChange = false
    if (index !== -1) {
      if (deviceSessionList[index].sessionId == deviceSession.sessionId) {
        // 同一个设备会话，刷新时间即可
        deviceSessionList[index].lastActiveTime = deviceSession.lastActiveTime
      } else {
        // 设备使用新的链接，旧的链接已经无用了
        g_utils.writeLog('释放旧的设备会话：', deviceSessionList[index])
        tcpServer.removeSession(deviceSessionList[index].sessionId)
    
        g_utils.writeLog('刷新设备新会话：', deviceSession)
        deviceSessionList[index].sessionId = deviceSession.sessionId
        deviceSessionList[index].ip = deviceSession.ip
        deviceSessionList[index].port = deviceSession.port
        deviceSessionList[index].lastActiveTime = deviceSession.lastActiveTime
    
        deviceInfoChange = true
      }
    } else {
      // 确认设备的链接权限
      if (deviceConnectRequestCallbackObj != null) {
        let { callbackFunc, context } = deviceConnectRequestCallbackObj
        let result = callbackFunc.call(context, deviceSession)
        g_utils.writeLog('设备请求链接回调结果：', result)
        if (!result) {
          // 非法设备，断开链接
          g_utils.writeLog('非法设备连接，断开链接：', deviceSession)
          tcpServer.removeSession(deviceSession.sessionId)
          return
        }
      }
      g_utils.writeLog('新增设备会话：', deviceSession)
      deviceSessionList.push(deviceSession)
      deviceInfoChange = true
    }
    
    if (deviceInfoChange) {
      if (deviceConnectStatusChangedCallbackObj != null) {
        let { callbackFunc, context } =
          deviceConnectStatusChangedCallbackObj
        callbackFunc.call(context, {
            "status": "connected",
            "deviceSn": deviceSn,
            "deviceAddress": deviceAddress
        })
      }
    }
    
    // 消息处理
    let msgEntity = new DeviceMessageReceive(
      packet.sn,
      packet.msgId,
      packet.msgBody,
      packet.seq
    )
    g_utils.writeLog('开始处理接收到的设备消息: ', msgEntity)
    processDeviceMessageReply(msgEntity)
    processDeviceMessageCallback(msgEntity)
  }

  /**
   * 处理接收到的设备消息的确认回复
   * 不同消息类型回复的内容不一样
   * @param {DeviceMessageReceive} msg
   */
  function processDeviceMessageReply(msg) {
    if (msg == null) {
      return
    }
    g_utils.writeLog('准备处理设备消息的确认回复')
    let replyMsg = null
    switch (msg.msgId) {
      case MsgType.DeviceBasicInfoReport:
      case MsgType.DeviceStatusReport:
        replyMsg = new DeviceMessageSend(
          msg.sn,
          msg.msgId,
          new CommonConfirmReplyMsgBody(1, 0),
          msg.seq+1
        )
        break
      // 自检上报
      case MsgType.StatusSelfCheckReport:
        // 回复收到的自检设备数量个数
        replyMsg = new DeviceMessageSend(
          msg.sn,
          msg.msgId,
          new CommonConfirmReplyMsgBody(1, msg.msgBody.itemList.length),
          msg.seq+1
        )
        break
      // 心跳回复
      case MsgType.Heartbeat:
        replyMsg = new DeviceMessageSend(
          msg.sn,
          msg.msgId,
          new HearbeatResponseMsgBody(2),
          msg.seq+1
        )
        break
        
      case MsgType.RecipeCookingStatus: // 炒菜进展
      case MsgType.RecipeFinishAutoWashPot: // 完成自动洗锅
        // 回复空消息体
        replyMsg = new DeviceMessageSend(
          msg.sn,
          msg.msgId,
          new CommonConfirmReplyMsgBody(null),
          msg.seq+1
        )
        break
      case MsgType.RecipeFinishCooking: // 完成炒菜
          replyMsg = new DeviceMessageSend(
            msg.sn,
            msg.msgId,
            new CommonConfirmReplyMsgBody(1, DeviceStatus.WaitPlating),
            msg.seq+1
          )
      case MsgType.ManualAddSeasoningResult: // 外设操作结果回复
          replyMsg = new DeviceMessageSend(
            msg.sn,
            msg.msgId,
            new CommonConfirmReplyMsgBody(null, null), // 确认回复，空body
            msg.seq+1
          )
        break
    }
    if (replyMsg != null) {
      g_utils.writeLog('回复给设备的消息实体: ', replyMsg)
      sendMessage(replyMsg)
        .then(r => {
          g_utils.writeLog('回复设备消息成功')
        })
        .catch(e => {
          g_utils.writeLog('回复设备消息错误:', e.message)
        })
    }
  }

  /**
   * 处理接收的设备消息的事件回调
   * @param {DeviceMessageReceive} msg
   */
  function processDeviceMessageCallback(msg) {
    if (msg == null) {
      return
    }
    g_utils.writeLog('准备处理设备消息的事件回调')
    let keySet = deviceMessageCallbackMap.keys()
    for (let key of keySet) {
      let { deviceSn, msgId, callbackFunc, context } =
        deviceMessageCallbackMap.get(key)
      if (callbackFunc) {
        if (deviceSn != null && deviceSn !== msg.sn) {
          continue
        }
        if (msgId != null && msgId != msg.msgId) {
          continue
        }
        g_utils.writeLog(
          '触发注册的事件回调：',
          `[key:${key}]-[sn:${msg.sn}]-[msgId:${msg.msgId}]`
        )
        try {
          callbackFunc.call(context, msg)
        } catch (error) {
          console.error('error in callback:', error)
        }
      }
    }
  }

  /**
   * 注册设备消息的回调
   * @param {String} deviceSn 设备SN，为空代表监听所有设备
   * @param {String} msgId 设备消息类型ID，为空代表监听所有消息类型
   * @param {Function} callback 回调函数
   * @param {Object} context 回调函数的this上下文
   */
  function registerDeviceMessageCallback(
    deviceSn,
    msgId,
    callback,
    context = null
  ) {
    if (typeof callback !== 'function') {
      throw new Error('callback must be a function')
    }
    let keyStr = simpleUUID()
    g_utils.writeLog(
      '注册事件回调: ',
      `[key:${keyStr}]-[sn:${deviceSn}]-[msgId:${msgId}]`
    )
    let callbackFunc = callback
    const callbackObj = {
      deviceSn,
      msgId,
      callbackFunc,
      context
    }
    deviceMessageCallbackMap.set(keyStr, callbackObj)
    return keyStr
  }
  /**
   * 注册设备消息的一次性回调，回调一次即被移除
   * @param {String} deviceSn 设备SN
   * @param {String} msgId 设备消息类型ID
   * @param {Function} callback 回调函数
   * @param {Object} context 回调函数的this上下文
   */
  function registerDeviceMessageOnceCallback(
    deviceSn,
    msgId,
    callback,
    context = null
  ) {
    if (typeof callback !== 'function') {
      throw new Error('callback must be a function')
    }
    let keyStr = simpleUUID()
    g_utils.writeLog(
      '注册一次性事件回调: ',
      `[key:${keyStr}]-[sn:${deviceSn}]-[msgId:${msgId}]`
    )
    let callbackFunc = args => {
      try {
        callback.call(context, args)
      } finally {
        g_utils.writeLog(
          '移除一次性事件回调: ',
          `[key:${keyStr}]-[sn:${deviceSn}]-[msgId:${msgId}]`
        )
        unregisterDeviceMessageCallback(keyStr)
      }
    }
    const callbackObj = {
      deviceSn,
      msgId,
      callbackFunc,
      context
    }
    deviceMessageCallbackMap.set(keyStr, callbackObj)
    return keyStr
  }

  /**
   * 注销回调函数
   * @param {String} key 注册的时候返回的key
   */
  function unregisterDeviceMessageCallback(key) {
    return deviceMessageCallbackMap.delete(key)
  }

  /**
   * 给设备发送消息
   * 使用promise返回下发消息成功或者失败
   * @param {DeviceMessageSend} data 消息，必须是DeviceMessageSend类型
   */
  function sendMessage(msg) {
    if (msg == null || !(msg instanceof DeviceMessageSend)) {
      return new Promise((resolve, reject) => {
        reject(new Error('参数错误'))
      })
    }
    g_utils.writeLog('send message to device')
    let deviceSn = msg.sn
    return new Promise((resolve, reject) => {
      if (!tcpServerRunning.value) {
        reject(new Error('TCP服务未启动'))
        return
      }
      let deviceSession = getSessionFromDeviceSn(deviceSn)
      if (deviceSession == null) {
        reject(new Error('设备会话不存在'))
        return
      }
      g_utils.writeLog('准备发送消息给这个设备会话: ', deviceSession)

      // 封装消息并编码
      let packet = new DevicePacket()
      let seq = msg.seq
      if (seq <= 0) {
        seq = deviceSession.nextSeq()
      }
      packet.setSendMsgInfo(seq, msg)
      let byteArray = DevicePacketCoderUtil.encode(packet)
      g_utils.writeLog('发送的Byte字节流为：', byteArray)
      let deviceMsg = {
        sessionId: deviceSession.sessionId,
        data: bytes2HexStr(byteArray)
      }
      g_utils.writeLog('发送的消息报文为：', deviceMsg)
      tcpServer.sendMessage(deviceMsg, result => {
        g_utils.writeLog('发送消息给设备的结果: ', result)
        if (result.code == 0) {
          resolve('success')
        } else {
          reject(new Error(result.msg))
          return
        }
      })
    })
  }

  /**
   * 发送消息给设备，同时传递一个断言函数用作设备回复消息的判断
   * 使用promise返回下发消息后，设备的回复是成功，或是失败
   * @param {DeviceMessageSend} msg 消息
   * @param {Function} predicate 断言函数，必须返回true或者false
   */
  function sendMessageWithReplyPredicate(msg, predicate) {
      return sendMessageWithReplyPredicateAndTimeout(msg, predicate, 10)
  }
  function sendMessageWithReplyPredicateAndTimeout(msg, predicate, timeSecond) {
    if (msg == null || !(msg instanceof DeviceMessageSend)) {
      return new Promise((resolve, reject) => {
        reject(new Error('参数错误'))
      })
    }
    if (typeof predicate !== 'function') {
      throw new Error('predicate must be a function')
    }
    let deviceSn = msg.sn
    return new Promise((resolve, reject) => {
      sendMessage(msg)
        .then(r => {
          let timeout = false
          // 发送消息后监听设备的确认回复消息
          let callbackFunc = replyMsg => {
            g_utils.writeLog('设备确认回复：', replyMsg)
            if (timeout) {
              g_utils.writeLog('设备确认回复已超时，无需处理')
              reject(new Error('等待设备确认回复超时'))
              return
            }
            // replyMsg 是一个 DeviceMessageReceive 数据类型
            if (predicate) {
              if (predicate(replyMsg)) {
                resolve('设备回复消息并断言成功')
                return
              }
            }
            reject(new Error('设备回复数据错误'))
            return
          }
          registerDeviceMessageOnceCallback(deviceSn, msg.msgId, callbackFunc)
          // 超时机制，超时没有回复当做失败
          setTimeout(() => {
            timeout = true
            reject(new Error('等待设备确认回复超时'))
            return
          }, timeSecond*1000)
        })
        .catch(e => {
          // 发送失败
          reject(e)
          return
        })
    })
  }

  // 停止TCP服务
  function stopTcpServer() {
    g_utils.writeLog('stop TCP Server')
    if (tcpServerRunning.value) {
      tcpServerRunning.value = false
      tcpServer.closeTcpServer()
    }
  }

  // 通过SN号获取设备会话
  function getSessionFromDeviceSn(deviceSn) {
    const index = deviceSessionList.findIndex(item => item.sn === deviceSn)
    if (index >= 0) {
      return deviceSessionList[index]
    }
    return null
  }
  
  // 关闭设备链接
  function closeDeviceConnection(deviceSn) {
      return new Promise((resolve, reject) => {
          let session = getSessionFromDeviceSn(deviceSn)
          if (session) {
              g_utils.writeLog(`close tcp connection for device=${session.sn} sessionId=${session.sessionId}`)
              // 断开链接
              tcpServer.removeSession(session.sessionId)
              resolve(session)
              return
          }
          reject("close device tcp error: device tcp not existed")
          return
      })
  }

  // 服务是否运行中
  function isTcpServerRunning() {
    return tcpServerRunning
  }
  function isTcpAlive() {
      return new Promise((resolve, reject) => {
          tcpServer.isTcpServerAlive(data => {
              resolve(data["alive"])
          })
      })
  }

  // 获取设备会话
  function getDeviceSessionList() {
    return deviceSessionList
  }

  // 返回API
  return {
    getDeviceSessionList,
    isTcpServerRunning,
    startTcpServer,
    stopTcpServer,
    isTcpAlive,
    sendMessage,
    sendMessageWithReplyPredicate,
    sendMessageWithReplyPredicateAndTimeout,
    registerDeviceMessageOnceCallback,
    registerDeviceMessageCallback,
    unregisterDeviceMessageCallback,
    registerDeviceConnectStatusChangedCallback,
    registerDeviceConnectRequestCallback,
    unregisterDeviceConnectRequestCallback,
    closeDeviceConnection
  }
})

export function useOutsideDeviceProtocolStore() {
  return useDeviceProtocolStore(g_store)
}
